#include <everest/dbc/odbc/odbc_common.h>
#include <vector>
#include <string.h>
#include <stdio.h>

using namespace NS_EVEREST::dbc::odbc;
using namespace NS_EVEREST::dbc;
using namespace NS_EVEREST;
using namespace std;


OdbcStatement::OdbcStatement(SQLHANDLE hStm, OdbcConnection* pCon)
: m_StmHandle(hStm), m_Conn(pCon)
{ /*EMPTY*/}

OdbcStatement::~OdbcStatement()
{
	try {
		this->close();
	} catch (...) {
		// EMPTY
	}
	m_Conn = NULL;
}

void OdbcStatement::close()
{
	OdbcException e;
	if ( m_StmHandle != SQL_NULL_HANDLE && m_StmHandle != SQL_INVALID_HANDLE ) {
		SQLRETURN ret = SQLFreeHandle(SQL_HANDLE_STMT, m_StmHandle);
		if ( ret == SQL_ERROR )
			e.set(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::close()");
		m_StmHandle = SQL_NULL_HANDLE;
	}
	if ( e.code() != 0 ) throw e;
	return ;
}

void OdbcStatement::release()
{
	delete this;
}

bool OdbcStatement::isClosed() const
{
	if ( m_StmHandle == SQL_NULL_HANDLE || m_StmHandle == SQL_INVALID_HANDLE)
		return true;
	else
		return false;
}

void OdbcStatement::addBatch(const char *pszSql)
{
	if ( m_SqlBatchList.empty())
		m_SqlBatchList.reserve(BATCH_SIZE);
	if ( m_SqlBatchList.size() == m_SqlBatchList.capacity())
		m_SqlBatchList.reserve(m_SqlBatchList.capacity() + BATCH_SIZE);

	m_SqlBatchList.push_back(String(pszSql));
}

int OdbcStatement::execute(const char *pszSql)
{
	SQLRETURN ret = SQLExecDirect(m_StmHandle, (SQLCHAR *)(pszSql?pszSql:""), SQL_NTS);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::execute(PCHAR)");
	else if ( ret == SQL_INVALID_HANDLE)
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::execute(PCHAR)");
	else if ( ret == SQL_STILL_EXECUTING)
		return EXEC_STILL;
	else if ( ret ==  SQL_NEED_DATA)
		return EXEC_NEED_DATA;
	else if ( ret == SQL_NO_DATA )
		return EXEC_NO_DATA;
	else if ( EVEREST_ODBC_SUCCESS(ret) ) {
		return EXEC_HAS_DATA;
	}

	return EXEC_UNKNOWN;
}

void OdbcStatement::cancel()
{
	SQLRETURN ret = SQLCancel(m_StmHandle);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::cancel()");
	else if ( ret == SQL_INVALID_HANDLE)
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::cancel()");

	return ;
}

IConnection * OdbcStatement::getConnection() const
{
	return this->m_Conn;
}

void OdbcStatement::clearBatch()
{
	this->m_SqlBatchList.clear();
}

void OdbcStatement::executeBatch(IntVector &results)
{
	results.reserve(this->m_SqlBatchList.size());
	StringVector::iterator iter = this->m_SqlBatchList.begin();
	while ( iter != this->m_SqlBatchList.end()) {
		String& sql = *iter;
		SQLRETURN ret = SQLExecDirect(m_StmHandle, (SQLCHAR *)(sql.c_str()), SQL_NTS);
		if (ret == SQL_ERROR) {
			results.push_back(EXEC_FAIL);
			throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeBatch(IntVector){1}");
		} else if ( ret == SQL_INVALID_HANDLE) {
			results.push_back(EXEC_FAIL);
			throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeBatch(IntVector)");
		} else if ( ret == SQL_STILL_EXECUTING) {
			results.push_back(EXEC_STILL);
		} else if ( ret == SQL_NEED_DATA) {
			results.push_back(EXEC_NEED_DATA);
		} else if ( ret == SQL_NO_DATA) {
			results.push_back(EXEC_NO_DATA);
		} else {
			SQLLEN len = 0;
			ret = SQLRowCount(m_StmHandle, &len);
			if ( ret == SQL_ERROR) {
				results.push_back(EXEC_FAIL);
				throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeBatch(IntVector){2}");
			} else if ( EVEREST_ODBC_SUCCESS(ret) ) {
				results.push_back((int)len);
			} else {
				results.push_back(EXEC_UNKNOWN);
			} // end if ret
		} // end if ret
		++iter;
	} // end while
} // void OdbcStatement::executeBatch(IntVector &results)

int OdbcStatement::executeUpdate(const char *pszSql)
{
	SQLRETURN ret = SQLExecDirect(m_StmHandle, (SQLCHAR *)(pszSql?pszSql:""), SQL_NTS);
	if (ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeUpdate(PCHAR){1}");
	} else if ( ret == SQL_INVALID_HANDLE ) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeUpdate(PCHAR)");
	} else if ( ret == SQL_STILL_EXECUTING) {
		return EXEC_STILL;
	} else if ( ret == SQL_NEED_DATA) {
		return EXEC_NEED_DATA;
	} else if ( ret == SQL_NO_DATA) {
		return EXEC_NO_DATA;
	} else {
		SQLLEN len = 0;
		ret = SQLRowCount(m_StmHandle, &len);
		if ( ret == SQL_ERROR) {
			throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeBatch(PCHAR){2}");
		} else if ( EVEREST_ODBC_SUCCESS(ret) ) {
			return (int)len;
		} else {
			return EXEC_UNKNOWN;
		} // if ret
	}
} // OdbcStatement::executeUpdate

IResultSet * OdbcStatement::executeQuery(const char *pszSql)
{
	SQLRETURN ret = SQLExecDirect(m_StmHandle, (SQLCHAR *)(pszSql?pszSql:""), SQL_NTS);
	if (ret == SQL_ERROR)
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeQuery(PCHAR){1}");
	else if ( ret == SQL_INVALID_HANDLE)
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeQuery(PCHAR){1}");


	SQLSMALLINT cols = 0;
	ret = SQLNumResultCols(m_StmHandle, &cols);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeQuery(PCHAR){2}");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeQuery(PCHAR){2}");
	} else if ( EVEREST_ODBC_SUCCESS(ret)) {
		return new OdbcResultSet(m_StmHandle, this->m_Conn, cols);
	}

	return new OdbcResultSet(m_StmHandle, this->m_Conn, 0, ret);
}

int OdbcStatement::getUpdateCount() const
{
	SQLLEN len = 0;
	SQLRETURN ret = SQLRowCount(m_StmHandle, &len);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeBatch(PCHAR){2}");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::executeQuery(PCHAR)");
	} else if ( EVEREST_ODBC_SUCCESS(ret) ) {
		return (int)len;
	}
	return EXEC_UNKNOWN;
}

int OdbcStatement::getQueryTimeout() const
{
	SQLULEN seconds = 0;
	SQLRETURN ret = SQLGetStmtAttr(m_StmHandle, SQL_ATTR_QUERY_TIMEOUT, &seconds, SQL_IS_UINTEGER, NULL);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getQueryTimeout()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getQueryTimeout()");
	} else {
		return (int)seconds;
	}
}

void OdbcStatement::setQueryTimeout(int seconds)
{
	ssize_t val = seconds;
	SQLRETURN ret = SQLSetStmtAttr(m_StmHandle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)val, SQL_IS_UINTEGER);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getQueryTimeout()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getQueryTimeout()");
	}
	return ;
}

IResultSet * OdbcStatement::getResultSet() const
{
	SQLSMALLINT cols = 0;
	SQLRETURN ret = SQLNumResultCols(m_StmHandle, &cols);

	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getResultSet()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getResultSet()");
	} else if ( EVEREST_ODBC_SUCCESS(ret) && cols > 0) {
		return new OdbcResultSet(m_StmHandle, this->m_Conn, cols);
	}

	return NULL;
}

bool OdbcStatement::getCursorScrollable() const
{
	SQLULEN scrollable;
	SQLRETURN ret = SQLGetStmtAttr(m_StmHandle, SQL_ATTR_CURSOR_SCROLLABLE, &scrollable, sizeof(SQLULEN), NULL);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getCursorScrollable()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getCursorScrollable()");
	}

	if ( scrollable == SQL_NONSCROLLABLE) return false;
	else return true;
}

void OdbcStatement::setCursorScrollable(bool scrollable)
{
	SQLULEN scroll = scrollable?SQL_SCROLLABLE:SQL_NONSCROLLABLE;
	SQLRETURN ret = SQLSetStmtAttr(m_StmHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER)scroll, sizeof(SQLULEN));
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::setCursorScrollable()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::setCursorScrollable()");
	}
}

void OdbcStatement::setCursorType(CursorType type)
{
	SQLULEN cursorType = SQL_CURSOR_FORWARD_ONLY;
	if ( type == CURSOR_STATIC ) cursorType = SQL_CURSOR_STATIC;
	else if ( type == CURSOR_DYNAMIC) cursorType = SQL_CURSOR_DYNAMIC;
	else if ( type == CURSOR_KEYSET) cursorType = SQL_CURSOR_KEYSET_DRIVEN;

	SQLRETURN ret = SQLSetStmtAttr(m_StmHandle, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)cursorType, 0);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::setCursorType()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::setCursorType()");
	}
}

CursorType OdbcStatement::getCursorType() const
{
	SQLULEN type;
	SQLRETURN ret = SQLGetStmtAttr(m_StmHandle, SQL_ATTR_CURSOR_TYPE, &type, sizeof(SQLULEN), NULL);
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getCursorScrollable()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::getCursorScrollable()");
	}

	if ( type == SQL_CURSOR_STATIC) return CURSOR_STATIC;
	else if ( type == SQL_CURSOR_DYNAMIC) return CURSOR_DYNAMIC;
	else if ( type == SQL_CURSOR_KEYSET_DRIVEN) return CURSOR_KEYSET;
	else return CURSOR_FORWARD_ONLY;
}

void OdbcStatement::closeCursor()
{
	SQLRETURN ret = SQLFreeStmt(this->m_StmHandle, SQL_CLOSE);  // CLOSE CURSOR
	if ( ret == SQL_ERROR) {
		throw OdbcException(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::closeCursor()");
	} else if ( ret == SQL_INVALID_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_STMT, m_StmHandle, "OdbcStatement::closeCursor()");
	}
}



